package com.logistics.inventory.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.logistics.inventory.entity.domain.Inventory;
import com.logistics.inventory.entity.domain.InventoryDetail;
import com.logistics.inventory.entity.model.*;
import com.logistics.inventory.mapper.InventoryMapper;
import com.logistics.map.entity.domain.MapComponents;
import com.logistics.map.entity.model.ComponentsEventRow;
import com.logistics.map.service.MapComponentsService;
import com.logistics.pick.entity.domain.PickContainer;
import com.logistics.pick.entity.domain.PickDetail;
import com.logistics.pick.service.PickContainerService;
import com.logistics.pick.service.PickDetailService;
import com.logistics.qrcode.service.QrcodeService;
import com.logistics.rcs.entity.model.RcsCoordinateRow;
import com.logistics.rcs.service.RcsService;
import com.logistics.utils.aspect.annotation.Insert;
import com.logistics.utils.aspect.annotation.Pager;
import com.logistics.utils.bean.ResultPager;
import com.logistics.utils.constants.Constants;
import com.logistics.utils.exception.MessageException;
import com.logistics.utils.socket.EventData;
import com.logistics.utils.socket.MenConns;
import com.logistics.utils.tool.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 库存 服务实现类
 * </p>
 *
 * @author kaihan.wang
 * @since 2024-06-28
 */
@Service
public class InventoryService extends ServiceImpl<InventoryMapper, Inventory> {
    @Autowired
    private InventoryMapper inventoryMapper;

    @Autowired
    private QrcodeService qrcodeService;

    @Autowired
    private InventoryDetailService inventoryDetailService;

    @Autowired
    private PickContainerService pickContainerService;

    @Autowired
    @Lazy
    private PickDetailService pickDetailService;

    @Autowired
    @Lazy
    private MapComponentsService mapComponentsService;

    @Autowired
    private RcsService rcsService;
    /**
     * 更新库存主数据状态
     * @param save
     */
    public void merge(InventorySave save){
        Inventory result = this.getOne(new LambdaQueryWrapper<Inventory>().eq(Inventory::getContainer, save.getContainer()));
        if(StringUtils.isNull(result)){
            this.save(Inventory.build(save));
        } else {
            this.update(Inventory.build(save), new LambdaQueryWrapper<Inventory>().eq(Inventory::getId, result.getId()));
        }
    }

    /**
     * 查找托盘位置
     */
    public List<InventoryContainerRow> containerPosition(){
        // 查找放在站点上面的托盘
        List<Inventory> inventories = this.list(new LambdaQueryWrapper<Inventory>().
                isNotNull(Inventory::getStation).ne(Inventory::getStation, ""));
        if(!inventories.isEmpty()){
            List<String> points = inventories.stream().map(Inventory::getStation).collect(Collectors.toList());
            List<MapComponents> locations = mapComponentsService.list(new LambdaQueryWrapper<MapComponents>().in(MapComponents::getLocation, points));
//            List<RcsCoordinateRow> coordinates = rcsService.coordinateRows(points);
            // 坐标点
            Map<String, MapComponents> coordinateRowMap = locations.stream().collect(Collectors.
                    toMap(MapComponents::getLocation, e -> e));
            // 查找托盘上的物料信息
            List<String> containers = inventories.stream().map(Inventory::getContainer).collect(Collectors.toList());
            List<InventoryDetail> inventoryDetails = inventoryDetailService.list(new LambdaQueryWrapper<InventoryDetail>().
                    in(InventoryDetail::getContainer,containers));
            Map<String, List<InventoryDetail>> goodMaps = inventoryDetails.stream().
                    collect(Collectors.groupingBy(InventoryDetail::getContainer));
            return inventories.stream().map(e ->
                    InventoryContainerRow.build(e, StringUtils.isNotNull(coordinateRowMap.get(e.getStation())) ?
                                    coordinateRowMap.get(e.getStation()).getX(): null, StringUtils.isNotNull(coordinateRowMap.get(e.getStation())) ?
                                    coordinateRowMap.get(e.getStation()).getY() : null,
                                    coordinateRowMap.get(e.getStation()),
                            StringUtils.isNotNull(goodMaps.get(e.getContainer())) ?
                                    goodMaps.get(e.getContainer()).stream().map(InventoryDetailRow::build).collect(Collectors.toList()) : null))
                    .collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 更新站点或托盘
     * @param params
     */
    @Transactional
    @Insert
    public void updateStationOrContainer(UpdStation params) throws MessageException {
        String point = qrcodeService.valueByCode(params.getStationCode(), Constants.CODE_TYPE_LOCATION);
        params.setStationCode(point);
        if(StringUtils.isNull(point)){
           throw new MessageException("不是有效的站点编码");
        }
        if(params.getIsFlag()){ // 根据托盘更改站点
            // 配送中的托盘不能更新
            PickContainer pickContainer = pickContainerService.getOne(new LambdaQueryWrapper<PickContainer>().
                    eq(PickContainer::getContainer, params.getContainerCode()).
                    eq(PickContainer::getStatus, Constants.DELIVERY_STATE_IN));
            if(StringUtils.isNotNull(pickContainer)){
                throw new MessageException("托盘正在配送中不能更新");
            }
            // 更新 inventory pickContainer
            this.update(Inventory.build(params, params.getIsFlag()), new LambdaQueryWrapper<Inventory>().
                    eq(Inventory::getContainer, params.getContainerCode()));
            pickContainerService.update(PickContainer.build(params, params.getIsFlag()), new LambdaQueryWrapper<PickContainer>().
                    eq(PickContainer::getContainer, params.getContainerCode())
                    .and(e -> e.eq(PickContainer::getStatus, Constants.PICK_STATE_IN)
                            .or().eq(PickContainer::getStatus, Constants.PICK_STATE_SUCCESS)
                    ));
        }else{ // 根据站点更改托盘
            // 配送中的托盘不能更新
            PickContainer pickContainer = pickContainerService.getOne(new LambdaQueryWrapper<PickContainer>().
                    eq(PickContainer::getStation, point).
                    eq(PickContainer::getStatus, Constants.DELIVERY_STATE_IN));
            if(StringUtils.isNotNull(pickContainer)){
                throw new MessageException("托盘正在配送中不能更新");
            }
            Inventory item = this.getOne(new LambdaQueryWrapper<Inventory>().eq(Inventory::getContainer, params.getContainerCode()));
            if(StringUtils.isNotNull(item)){
                throw new MessageException("托盘已经存在绑定不能做更新");
            }
            // 更新 inventory inventoryDetail pickContainer pickDetail
            Inventory inventory = this.getOne(new LambdaQueryWrapper<Inventory>().eq(Inventory::getStation, point));
            this.update(Inventory.build(params, params.getIsFlag()), new LambdaQueryWrapper<Inventory>().
                    eq(Inventory::getId, inventory.getId()));
            inventoryDetailService.update(InventoryDetail.build(params), new LambdaQueryWrapper<InventoryDetail>().
                    eq(InventoryDetail::getContainer, inventory.getContainer()));
            PickContainer pick = pickContainerService.getOne(new LambdaQueryWrapper<PickContainer>().eq(PickContainer::getStation, point)
                    .and(e -> e.eq(PickContainer::getStatus, Constants.PICK_STATE_IN).
                            or().eq(PickContainer::getStatus, Constants.PICK_STATE_SUCCESS)));
            pickContainerService.update(PickContainer.build(params, params.getIsFlag()), new LambdaQueryWrapper<PickContainer>().
                    eq(PickContainer::getStation, point).and(e -> e.eq(PickContainer::getStatus, Constants.PICK_STATE_IN).
                            or().eq(PickContainer::getStatus, Constants.PICK_STATE_SUCCESS)));
            if(StringUtils.isNotNull(pick)){
                pickDetailService.update(PickDetail.build(params), new LambdaQueryWrapper<PickDetail>()
                        .eq(PickDetail::getPick, pick.getPick()).eq(PickDetail::getContainer, pick.getContainer()));
            }
            // 释放站点
            mapComponentsService.update(MapComponents.build(new BigDecimal(0), 0, false),
                    new LambdaQueryWrapper<MapComponents>().eq(MapComponents::getLocation, point));
        }
    }

    /**
     * 删除站点或托盘
     * @param code
     * @param isFlag
     */
    public void  deleteStationOrContainer(String code, String isFlag) throws MessageException {
        String point = qrcodeService.valueByCode(code, Constants.CODE_TYPE_LOCATION);
        String container = code;
        if (StringUtils.isNull(point)){
            Inventory inventory = this.getOne(new LambdaQueryWrapper<Inventory>().eq(Inventory::getContainer, code));
            if(StringUtils.isNotNull(inventory)){
                point = inventory.getStation();
            }
            container = inventory.getContainer();
        }
        // 查找出站点
        MapComponents location = mapComponentsService.getLocationByStation(point);
        // 清空站点
        if(StringUtils.isEquals(isFlag, "1")){
            //验证站点是否处于锁定状态
            if (location.getLocationLock()){
                throw new MessageException("站点被锁定");
            }
            PickContainer pickContainer = pickContainerService.getOne(new LambdaQueryWrapper<PickContainer>()
                    .eq(PickContainer::getStation, point).ne(PickContainer::getStatus, Constants.DELIVERY_STATE_SUCCESS));
            if(StringUtils.isNotNull(pickContainer)){
                throw new MessageException("拣配单没配送完成不能清空");
            }
            List<Inventory> item = this.list(new LambdaQueryWrapper<Inventory>().eq(Inventory::getStation, point));
            if(!item.isEmpty()){
//                this.remove(new LambdaQueryWrapper<Inventory>().in(Inventory::getId, item.stream().map(Inventory :: getId).collect(Collectors.toList())));
                // 创建 LambdaUpdateWrapper 对象
                LambdaUpdateWrapper<Inventory> updateWrapper = Wrappers.lambdaUpdate();

                // 设置批量更新条件
                updateWrapper.in(Inventory::getId, item.stream().map(Inventory :: getId).collect(Collectors.toList()))
                        .set(Inventory::getStation, "");
                this.update(updateWrapper);
                inventoryDetailService.remove(new LambdaQueryWrapper<InventoryDetail>().in(InventoryDetail::getContainer,
                        item.stream().map(Inventory :: getContainer).collect(Collectors.toList())));
            }
            // 释放站点
            mapComponentsService.update(MapComponents.build(new BigDecimal(0), 0, false),
                    new LambdaQueryWrapper<MapComponents>().eq(MapComponents::getLocation, point));
        }else if(StringUtils.isEquals(isFlag, "0")){
            PickContainer pickContainer = pickContainerService.getOne(new LambdaQueryWrapper<PickContainer>()
                    .eq(PickContainer::getContainer, code).ne(PickContainer::getStatus, Constants.DELIVERY_STATE_SUCCESS));
            if(StringUtils.isNotNull(pickContainer)){
                throw new MessageException("拣配单没配送完成不能清空");
            }
            inventoryDetailService.remove(new LambdaQueryWrapper<InventoryDetail>().eq(InventoryDetail::getContainer, code));
        }
        // 推送消息到前端
//        ComponentsEventRow data = ComponentsEventRow.build(container, location, null);
//        RcsCoordinateRow row = rcsService.coordinateRow(location.getLocation().split("-")[1]);
//        data.setX(row.getX());
//        data.setY(row.getY());
//        MenConns.sendText("__clear_location", data);
    }

    /**
     * 校验该站点是否已经存在托盘
     * @param station
     * @throws MessageException
     */
    public void checkSite(String station) throws MessageException {
        List<Inventory> inventories = this.list(new LambdaQueryWrapper<Inventory>().eq(Inventory :: getStation,station));

        if (!inventories.isEmpty()){
            throw new MessageException("该站点已经存在托盘");
        }
    }

    /**
     * 寻找空托盘
     * @return
     */
    public List<Inventory> findEmptyTray() throws MessageException {
        List<Inventory> inventoryList = this.list(new LambdaQueryWrapper<Inventory>()
                .eq(Inventory::getIsEmpty, 1)
                .isNotNull(Inventory::getStation));

        if (inventoryList.isEmpty()) {
            throw new MessageException("空托存放区暂无空托盘，请稍后");
        }

        return inventoryList;
    }

    /**
     * 查询站点是否有绑定的托盘,有托盘不可进入空托呼叫
     * @param station
     * @return
     * @throws MessageException
     */
    public boolean checkStationContainer(String station) throws MessageException {
        String value = qrcodeService.valueByCode(station, Constants.CODE_TYPE_LOCATION);
        Inventory inventory = this.getOne(new LambdaQueryWrapper<Inventory>()
                .eq(Inventory::getStation, value));
        if(StringUtils.isNotNull(inventory)){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 工序产出，根据站点，查看托盘来料配送物料信息
     *
     * @param station
     * @return
     */
    public List<InventoryDetail> findStationInventory(String station) throws MessageException {
        String value = qrcodeService.valueByCode(station, Constants.CODE_TYPE_LOCATION);
        Inventory inventory = this.getOne(new LambdaQueryWrapper<Inventory>()
                .eq(Inventory::getStation, value));
        List<InventoryDetail> inventoryDetailList = inventoryDetailService.list(new LambdaQueryWrapper<InventoryDetail>()
                .in(InventoryDetail::getContainer, inventory.getContainer()));
        //配送状态条件 待字段添加后


        return inventoryDetailList;
    }

    /**
     * 获取站点上的空托
     */
    public List<Inventory> checkEmptyTray(String staion) {
        return this.list(new LambdaQueryWrapper<Inventory>()
                .eq(Inventory::getIsEmpty, 1)
                .eq(Inventory :: getStation, staion));
    }

    public List<Inventory> findAllStation() {
        return this.list();
    }

    /**
     * 查找站点中小于改层级的托盘
     * @param location
     * @param level
     */
    public List<Inventory> findTrayByLevel(String location, int level) {
        return this.list(new LambdaQueryWrapper<Inventory>()
                .eq(Inventory :: getStation, location).gt(Inventory :: getContainerLevel, level));
    }

    /**
     * 查询库存
     * @param inventoryPage
     * @return
     */
    @Pager
    public ResultPager getInventoryPage(InventoryPage inventoryPage) {
        IPage page =  new Page(inventoryPage.getCurrent(),inventoryPage.getPageSize());
        inventoryPage.setCurrent((int)page.offset());
        List<Inventory> inventoryList =inventoryMapper.findInventory(inventoryPage);
        int total = inventoryMapper.getContainerCount(inventoryPage);
        return new ResultPager(inventoryList.stream().map(e->InventoryRow.build(e)).collect(Collectors.toList()), total);
    }

    /**
     *站点清空接口
     * @param location 站点
     * @return
     */
    public List<ComponentsEventRow> clearLocation(String location) {
        MapComponents mapComponents = mapComponentsService.getOne(new LambdaQueryWrapper<MapComponents>()
                .eq(MapComponents::getLocation, location)
                .eq(MapComponents::getClazz, Constants.COMPONENTS_TYPE_LOCATION));
        return this.list(new LambdaQueryWrapper<Inventory>().eq(Inventory::getStation, location)).stream()
                .map(item -> ComponentsEventRow.build(item.getContainer(), mapComponents, null)).collect(Collectors.toList());
    }


    public List<Inventory> getInventoryByPoint(List<String> point) {
        return this.list(new LambdaQueryWrapper<Inventory>().in(Inventory :: getStation, point));
    }

}
